#include <QtTest>
#include <QCoreApplication>

// add necessary includes here
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include "nvjpeg2k.h"

#include "CodecNvJPEG2000.h"
#include "opencv2/opencv.hpp"
#include "opencv2/core/utils/logger.hpp"
#include "opencv2/imgcodecs.hpp"

class codecJPEGTest : public QObject
{
    Q_OBJECT

public:
    codecJPEGTest();
    ~codecJPEGTest();

private slots:
    void test_codec();
    void test_CVencBenchmark();
    void test_CVdecBenchmark();
    void test_nvJP2KencBenchmark();
    void test_nvJP2KdecBenchmark();
    void test_vsCV();

private:
    int tbuf_w = 1571;
    int tbuf_h = 512;
    unsigned short * tbuf = nullptr;
    cv::Mat srcimg;
};

codecJPEGTest::codecJPEGTest()
{
    cv::utils::logging::setLogLevel(cv::utils::logging::LOG_LEVEL_WARNING);  //  警告级以上信息才显示
    srcimg = cv::imread("00001.png",cv::IMREAD_UNCHANGED);
    tbuf_w = srcimg.cols;
    tbuf_h = srcimg.rows;
    tbuf = reinterpret_cast<unsigned short *>(srcimg.data);

//    tbuf_w = 500;
//    tbuf_h = 512;
//    tbuf = new unsigned short[tbuf_w * tbuf_h];
//    for (int i = 0; i < tbuf_h * tbuf_w; ++i) {
//        tbuf [i] = (unsigned short)(qrand()%65536);
//    }
}

codecJPEGTest::~codecJPEGTest()
{
//    if(tbuf) delete tbuf;
}

void codecJPEGTest::test_codec()
{

    JP2_U16C1Encoder jp2_u16encoder(tbuf_w,tbuf_h);
//    jp2_u16encoder.setLossy();
    QVERIFY(jp2_u16encoder.doEncode(tbuf,70));
    size_t compressed_size = 0;
    const char * compressed_data = nullptr;
    compressed_data = jp2_u16encoder.getOutputBuffer(compressed_size);
    QFile file("test.jp2");
    file.open(QFile::WriteOnly|QFile::Truncate);
    file.write(compressed_data,static_cast<qint64>(compressed_size));
    file.close();

    JP2_Decoder jp2_decoder;
    QVERIFY(jp2_decoder.parseEncodedBuf(compressed_data,compressed_size));
    QVERIFY(jp2_decoder.doDecode());

    size_t uncompressed_size = 0;
    const char * uncompressd_buf = jp2_decoder.getDecodedRes(uncompressed_size);
    QVERIFY(uncompressd_buf != nullptr);
    {
        const unsigned short * outbuf = reinterpret_cast<const unsigned short*>(uncompressd_buf);
        int neqsum = 0;
        int maxdiff = 0;
        float percentdiff = 0;
        for (int i = 0; i < tbuf_w * tbuf_h; ++i) {
            int d = tbuf[i];
            d = d - outbuf[i];
            if(d<0) d = -d;
            neqsum += d;
            percentdiff += d != 0;
            if(d > maxdiff) maxdiff = d;
        }
        qDebug()<< "mean diff = " << static_cast<float>(neqsum) / static_cast<float>(tbuf_w * tbuf_h);
        qDebug()<< "diff percent = " << percentdiff / static_cast<float>(tbuf_w * tbuf_h) << "%";
        qDebug()<< "max diff = " << maxdiff;
    }
}

void codecJPEGTest::test_CVencBenchmark()
{
    cv::Mat srcimg(tbuf_h,tbuf_w,CV_16UC1,tbuf);
    std::vector<unsigned char> cvencode;
    QBENCHMARK{
        QVERIFY(cv::imencode(".jp2",srcimg,cvencode,{cv::IMWRITE_JPEG2000_COMPRESSION_X1000,800}));
    }
}

void codecJPEGTest::test_CVdecBenchmark()
{
    QFile file("test.jp2");
    file.open(QFile::ReadOnly);
    auto ba = file.readAll();
    file.close();
    cv::Mat jp2f(1,ba.size(),CV_8UC1,ba.data());

    QBENCHMARK{
        auto decodebycv = cv::imdecode(jp2f,cv::IMREAD_UNCHANGED);
        QVERIFY(!decodebycv.empty() && decodebycv.data);
    }
}

void codecJPEGTest::test_nvJP2KencBenchmark()
{
    JP2_U16C1Encoder jp2_u16encoder(tbuf_w,tbuf_h);
//    jp2_u16encoder.setLossy();
    QBENCHMARK{
        QVERIFY(jp2_u16encoder.doEncode(tbuf,80));
        size_t compressed_size = 0;
        const char * compressed_data = nullptr;
        compressed_data = jp2_u16encoder.getOutputBuffer(compressed_size);
    }
}

void codecJPEGTest::test_nvJP2KdecBenchmark()
{
    QFile file("test.jp2");
    file.open(QFile::ReadOnly);
    auto ba = file.readAll();
    file.close();

    JP2_Decoder jp2_decoder;

    QBENCHMARK{
        QVERIFY(jp2_decoder.parseEncodedBuf(ba.data(),
                                            static_cast<size_t>(ba.size()))
                );
        QVERIFY(jp2_decoder.doDecode());
        int w=0,h=0;
        jp2_decoder.getOutputInfo(w,h);
        size_t uncompressed_size = 0;
        const char * uncompressd_buf = jp2_decoder.getDecodedRes(uncompressed_size);
        QVERIFY(uncompressd_buf != nullptr);
        QVERIFY(uncompressed_size == static_cast<size_t>(w * h * 2));
    }
}

void codecJPEGTest::test_vsCV()
{
    JP2_U16C1Encoder jp2_u16encoder(tbuf_w,tbuf_h);
//    jp2_u16encoder.setLossy();
    QVERIFY(jp2_u16encoder.doEncode(tbuf,100));
    size_t compressed_size = 0;
    const char * compressed_data = nullptr;
    compressed_data = jp2_u16encoder.getOutputBuffer(compressed_size);

    cv::Mat nvcdata(1,static_cast<int>(compressed_size),
                    CV_8UC1,(void *)(compressed_data)
                    );
    auto decodebycv = cv::imdecode(nvcdata,cv::IMREAD_UNCHANGED);
    QVERIFY(!decodebycv.empty() && decodebycv.data);
    cv::imshow("encode by nvJPEG2000,decode by cv",decodebycv);
    cv::waitKey(1000);

    cv::Mat srcimg(tbuf_h,tbuf_w,CV_16UC1,tbuf);
    std::vector<unsigned char> cvencode;
    QVERIFY(cv::imencode(".jp2",srcimg,cvencode,{cv::IMWRITE_JPEG2000_COMPRESSION_X1000,1000}));
    JP2_Decoder jp2_decoder;
    QVERIFY(jp2_decoder
            .parseEncodedBuf(reinterpret_cast<const char *>(cvencode.data()),cvencode.size())
            );
    QVERIFY(jp2_decoder.isU16C1Image());
    QVERIFY(jp2_decoder.doDecode());

    int w=0,h=0;
    jp2_decoder.getOutputInfo(w,h);
    size_t uncompressed_size = 0;
    const char * uncompressd_buf = jp2_decoder.getDecodedRes(uncompressed_size);
    QVERIFY(uncompressd_buf != nullptr);
    QVERIFY(uncompressed_size == static_cast<size_t>(w * h * 2));

    cv::Mat decodebynv(h,w,CV_16UC1,(void *)uncompressd_buf);
    cv::imshow("encode by cv,decode by nvJPEG2000",decodebynv);
    cv::waitKey(3000);
}



QTEST_MAIN(codecJPEGTest)

#include "tst_encodetest.moc"
